﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Threading;
using System.Timers;

namespace AsicMonitor
{
    public partial class MonitorView : Form
    {
        private Dictionary<string, Miner> minerDict;
        private HashSet<Miner> existMinerSet;
        private Dictionary<Miner, DataGridViewRow> minerRowMap;
        private HashSet<Miner> updateMinerSet;

        private string spaceName;
        private List<String> ipRange;
        private string currentKeyword;

        private System.Timers.Timer refreshUITimer;
        private System.Timers.Timer heartbeatTimer;
        private System.Timers.Timer reconnectTimer;

        public MonitorView()
        {
            spaceName = "";
            ipRange = new List<string>();
            minerDict = new Dictionary<string, Miner>();
            existMinerSet = new HashSet<Miner>();
            minerRowMap = new Dictionary<Miner, DataGridViewRow>();
            updateMinerSet = new HashSet<Miner>();

            InitializeComponent();

            this.refreshMinersUIHandler += this.OnRefreshMinersUI;
            this.minerGridView.Scroll += minerGridView_Scroll;

            init();
        }

        private void loadConfig()
        {
            this.spaceName = AppConfig.GetAppConfig(nameof(spaceName));
            string countStr = AppConfig.GetAppConfig("iprange_count");
            if (countStr == null || countStr == "") return;

            int count = 0;
            if(int.TryParse(countStr, out count))
            {
                if (count <= 0) return;
                for(int i =0; i < count; i++)
                {
                    string range = AppConfig.GetAppConfig($"iprange_{i}");
                   

                    if (range == null || range == "") continue;
                    Console.WriteLine($"read ip {range}");
                    ipRange.Add(range);
                }
            }           
        }

        private void init()
        {
            ipRangeCombox.Items.Clear();
            ipRangeCombox.Items.Add("全部");
            Win32Utility.SetCueText(keywordTxt, "请输入要搜索的IP");

            loadConfig();
            refreshConfigUI();

            if (this.spaceName == null || this.spaceName == "")
            {
                this.showSpaceDailog();
            }

            
            foreach (string range in ipRange)
            {
                addNewIpRange(range);
            }
            ipRangeCombox.SelectedItem = "全部";

            // start to refresh 
            refreshUITimer = new System.Timers.Timer(5000);
            refreshUITimer.Elapsed += new ElapsedEventHandler((sender, e) => {
                Miner[] miners = null;
                lock (updateMinerSet)
                {
                    miners = updateMinerSet.ToArray();
                    updateMinerSet.Clear();
                }
                Console.WriteLine("当前线程ID = " + Thread.CurrentThread.ManagedThreadId); // 是子线程
                this.Invoke(new RefreshMinersUI(OnRefreshMinersUI), new Object[] { minerDict.Values.ToArray<Miner>() });
                // scan ip
            });
            refreshUITimer.AutoReset = true;
            refreshUITimer.Enabled = true;

            // send heartbeat.
            heartbeatTimer = new System.Timers.Timer(1 * 60 * 1000);
            heartbeatTimer.Elapsed += new ElapsedEventHandler((sender, e) => {
                Log.Debug("start to send hearbeat msg");
                foreach (Miner miner in minerDict.Values.ToArray<Miner>())
                {
                    miner.HealthCheck();
                }

            });
            heartbeatTimer.AutoReset = true;
            heartbeatTimer.Enabled = true;

            // reconnect timer
            reconnectTimer = new System.Timers.Timer(3 * 60 * 1000);
            reconnectTimer.Elapsed += new ElapsedEventHandler((sender, e) => {
                Log.Debug("start to reconnect if offline");
                foreach (Miner miner in minerDict.Values.ToArray<Miner>())
                {
                    miner.reconnectIfOffline();
                }
            });
            reconnectTimer.AutoReset = true;
            reconnectTimer.Enabled = true;
        }

        private void OnCellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
        {
            throw new NotImplementedException();
        }



        private void saveConfig()
        {
            AppConfig.UpdateAppConfig(nameof(spaceName), spaceName);
            AppConfig.UpdateAppConfig("iprange_count", ipRange.Count.ToString());
            int i = 0;
            foreach (string range in ipRange)
            {
                AppConfig.UpdateAppConfig($"iprange_{i++}", range);
            }
        }

        private void refreshConfigUI()
        {
            this.Text = $"POWer监控助手--{spaceName}";

            foreach (String range in ipRange)
            {
                if (!ipRangeCombox.Items.Contains($"{range}.*"))
                {
                    ipRangeCombox.Items.Add($"{range}.*");
                    Log.Debug($"add new ip => {range}");
                }
            }
            ipRangeCombox.SelectedValueChanged += (sender, e) =>
            {
                string value = ipRangeCombox.SelectedItem.ToString();
                value = value.Replace("全部", "");
                value = value.Replace("*", "");
                searchByKeyword(value);
            };
            ipRangeCombox.Refresh();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            
        }


        private void minerGridView_Scroll(object sender, ScrollEventArgs e)
        {
            Rectangle rect;

            DataGridView ctrl = (DataGridView)sender;
            Point pt = PointToScreen(ctrl.Location);

            if (pt.X < 0)
            {
                int left = -pt.X;
                int top = ctrl.ColumnHeadersHeight;
                int width = e.OldValue - e.NewValue;
                int height = ctrl.ClientSize.Height;
                ctrl.Invalidate(new Rectangle(new Point(left, top), new Size(width, height)));
            }

            pt.X += ctrl.Width;
            rect = Screen.GetBounds(pt);

            if (pt.X > rect.Right)
            {
                int left = ctrl.ClientSize.Width - (pt.X - rect.Right) - (e.NewValue - e.OldValue);
                int top = ctrl.ColumnHeadersHeight;
                int width = e.NewValue - e.OldValue;
                int height = ctrl.ClientSize.Height;
                ctrl.Invalidate(new Rectangle(new Point(left, top), new Size(width, height)));
            }

            pt.Y += ctrl.Height;
            if (pt.Y > rect.Bottom)
            {
                int left = 0;
                int top = ctrl.ColumnHeadersHeight;
                int width = ctrl.ClientSize.Width;
                int height = ctrl.ClientSize.Height - (pt.Y - rect.Bottom) - (e.NewValue - e.OldValue);
                ctrl.Invalidate(new Rectangle(new Point(left, top), new Size(width, height)));
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if(sender == addIpBtn)
            {
                this.showAddIpDailog();
                return;
            }

            if (sender == rescanBtn)
            {
                // 重新搜索....
                rescanMiner();
                return;
            }

            if (sender == searchBtn)
            {
                // 重新搜索....
                string keyword = keywordTxt.Text;
                searchByKeyword(keyword); 
                return;
            }

            if(sender == spaceBtn)
            {
                this.showSpaceDailog();
                return;
            }
            // searchBtn
        }

        private void rescanMiner()
        {
            this.keywordTxt.Text = null;

            foreach(Miner miner in existMinerSet)
            {
                miner.StopWatch();
            }
            existMinerSet.Clear();
            minerGridView.Rows.Clear();
            minerDict.Clear();
            minerRowMap.Clear();

            foreach (string range in ipRange)
            {
                addNewIpRange(range);
            }
            ipRangeCombox.SelectedItem = "全部";
        }

        private void showAddIpDailog()
        {
            AddIpDailog dailog = new AddIpDailog();
            dailog.addNewIpEvent += (ip1, ip2, ip3) =>
            {
                string range = $"{ip1}.{ip2}.{ip3}";
                this.addNewIpRange(range);
            };
            dailog.removeNewIpEvent += (ip1, ip2, ip3) =>
            {
                string range = $"{ip1}.{ip2}.{ip3}";
                this.removeNewIpRange(range);
            };

            dailog.StartPosition = FormStartPosition.CenterParent;
            dailog.ShowDialog();
        }

        private void showSpaceDailog()
        {
            SpaceDailog spaceDailog = new SpaceDailog(spaceName);
            spaceDailog.StartPosition = FormStartPosition.CenterParent;
            spaceDailog.saveSpceNameEvent += (newName) =>
            {
                this.spaceName = newName;
                this.Text = "POWer监控助手--" + spaceName;
                this.saveConfig();
                this.refreshConfigUI();
                foreach(Miner miner in minerDict.Values.ToArray())
                {
                    miner.Space = this.spaceName;
                }
            };
            spaceDailog.ShowDialog();
        }

        private void searchByKeyword(string keyword)
        {
            this.currentKeyword = keyword;
            OnRefreshMinersUI(this.existMinerSet.ToArray());
        }

        protected void addNewIpRange(string range)
        {
            // string range = "127.0.0";
            if (!ipRange.Contains(range))
            {
                ipRange.Add(range);
                this.saveConfig();
                this.refreshConfigUI();
            }
            this.currentKeyword = range;
            this.keywordTxt.Text = null;
            this.ipRangeCombox.SelectedItem = $"{range}.*";

            for (int i = 0; i < 255; i++)
            {
                string ip = range + "." + i;

                if (minerDict.ContainsKey(ip)) continue;

                // Miner miner = new Miner(ip + ":4949/Test");
                Miner miner = new Miner(ip);
                miner.Space = this.spaceName;
              //  miner.DataModifyEvent += this.MinerDataModifyHandle;
                miner.StartWatch();

                Console.WriteLine(ip);

                minerDict.Add(ip, miner);

                OnRefreshMinerUI(miner);
            }
        }

        protected void removeNewIpRange(string range)
        {
            if (!ipRange.Contains(range))
            {
                return;
            }

            ipRange.Remove(range);
            this.saveConfig();
            this.refreshConfigUI();

            if(this.currentKeyword == range)
            {
                this.currentKeyword = "";
                this.keywordTxt.Text = null;
            }
            this.ipRangeCombox.Items.Remove($"{range}.*");

            foreach(Miner miner in minerDict.Values.ToArray())
            {
                if (miner.IP.StartsWith(range))
                {
                    miner.Close();
                }
            }
            OnRefreshMinersUI(this.existMinerSet.ToArray());
        }

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if(e.ColumnIndex == 0)
            {
                if ((bool)minerGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue == true)
                {
                    this.minerGridView.Rows[e.RowIndex].Cells[0].Value = false;
                }
                else
                {
                    this.minerGridView.Rows[e.RowIndex].Cells[0].Value = true;
                }
                RefreshSelectedItemStatusUI();
            }

            // 只处理最后一栏，详情页的更新
            if(e.ColumnIndex == 11)
            {
                DataGridViewRow row = minerGridView.Rows[e.RowIndex];
                // find the miner
                foreach (KeyValuePair<Miner, DataGridViewRow> kvp in minerRowMap)
                {
                    if (kvp.Value == row)
                    {
                        // 弹出 miner 对应的详情页
                        System.Diagnostics.Process.Start("http://" + kvp.Key.IP);
                        return;
                    }
                }
            }
        }

       

        private void MinerDataModifyHandle(object sender, Miner.MinerDataEventArgs e)
        {
            Miner miner = (Miner)sender;
            lock (updateMinerSet) {
                if (!updateMinerSet.Contains(miner))
                {
                    updateMinerSet.Add(miner);
                }
            }
        }



        public delegate void RefreshMinersUI(Miner[] miners);
        private RefreshMinersUI refreshMinersUIHandler;


        private void OnRefreshMinersUI(Miner[] miners)
        {
            foreach(Miner miner in miners)
            {
                OnRefreshMinerUI(miner);
            }
            this.RefreshTotalLabelUI();
            this.RefreshSelectedItemStatusUI();
        }


        /// <summary>
        ///  当Miner信息变动时，刷新Miner的显示信息
        ///  TODO 添加延迟更新，当miner太多时
        /// </summary>
        /// <param name="miner">变动的Miner</param>
        private void OnRefreshMinerUI(Miner miner)
        {
            string ip = miner.IP;
            if (this.currentKeyword != null && this.currentKeyword != "" && !ip.StartsWith(this.currentKeyword))
            {// 直接过滤不再显示的范围内的 miner 数据
                if (existMinerSet.Contains(miner))
                {
                    if (minerRowMap.ContainsKey(miner))
                    {
                        var minerRow = minerRowMap[miner];
                        minerRowMap.Remove(miner);
                        if (minerRow != null)
                        {
                            this.minerGridView.Rows.Remove(minerRow);
                        }
                    }
                }
                // skip this.
                return;
            }

            var status = miner.getState();
            // if (status == "init" || status == "connecting") return;
            if(status == "nonexist"){
                // 若miner检测之后，不存在，则直接移除就ok
                if (existMinerSet.Contains(miner)
                    && minerRowMap.ContainsKey(miner) 
                    && minerRowMap[miner] != null)
                {
                    var minerRow = minerRowMap[miner];
                    this.minerGridView.Rows.Remove(minerRow);
                    minerRowMap.Remove(miner);
                }
                return;
            }

            DataGridViewRow row = null;
            if (!existMinerSet.Contains(miner) 
                || !minerRowMap.ContainsKey(miner)
                || minerRowMap[miner] == null)
            { // 当miner存在时，创建对应Row
                int index = this.minerGridView.Rows.Add();
                row = this.minerGridView.Rows[index];
                existMinerSet.Add(miner);
                minerRowMap.Add(miner, row);
            }else{// 否则，使用之前创建好了的Row
                row = minerRowMap[miner];
            }

            // 刷新miner这行的内容
            row.Cells[1].Value = row.Index + 1;
            if (row.Cells[3].Value == null ) {
                row.Cells[3].Value = miner.IP;
            }
            if(miner.Conf != null)
            {
                if ( miner.Conf.miner_name != null && (row.Cells[2].Value == null || row.Cells[2].Value.ToString() != $"{miner.Conf.miner_name}")) row.Cells[2].Value = $"{miner.Conf.miner_name}";
                if ( miner.Conf.user != null && (row.Cells[4].Value == null || row.Cells[4].Value.ToString() != $"{miner.Conf.user}")) row.Cells[4].Value = $"{miner.Conf.user}";
                if ( miner.Conf.url != null && (row.Cells[5].Value == null || row.Cells[5].Value.ToString() != $"{miner.Conf.url}")) row.Cells[5].Value = $"{miner.Conf.url}";
            }
            

            if (miner.Dat != null){
                //   Send(" {\"get\": \"stat\", \"dat\": { \"uptime\": 890868, \"temp\": 40, \"diff\": 256.000000, \"hashrate\": [403.00,501.08,310.19,329.28,386.55,381.77,386.55,375.81,388.14,395.49,387.54,389.38,389.06], \"accept\": 45206, \"reject\": 174, \"wrong\": 26, \"err\": 0}}");
                if (miner.Dat.hashrate != null && (row.Cells[6].Value == null || row.Cells[6].Value.ToString() != miner.Dat.hashrate[0].ToString().ToHashrate()))
                {
                    row.Cells[6].Value = miner.Dat.hashrate[0].ToString().ToHashrate();
                }
                if (miner.Dat.temp  > 0 && (row.Cells[7].Value == null || row.Cells[7].Value.ToString() != miner.Dat.temp.ToString().ToTemp()))
                {
                    row.Cells[7].Value = miner.Dat.temp.ToString().ToTemp();
                }
                if (miner.Dat.uptime > 0 && (row.Cells[8].Value == null || row.Cells[8].Value.ToString() != miner.Dat.uptime.ToString().ToDayTime()))
                {
                    row.Cells[8].Value = miner.Dat.uptime.ToString().ToDayTime();
                }
                if (miner.Dat.accept != 0 && miner.Dat.reject != 0)
                {
                    double rate = Math.Round((double)miner.Dat.accept * 100 / (miner.Dat.accept + miner.Dat.reject), 1);
                    row.Cells[9].Value = $"{rate} %";
                }
            }
            row.Cells[10].Value = miner.getState();
            if(row.Cells[11].Value == null) row.Cells[11].Value = "详情";

            if(miner.getState() == "offline")
            {
                row.DefaultCellStyle.BackColor = Color.Brown;
            }
            else
            {
                row.DefaultCellStyle.BackColor = Color.White;
            }
        }


        /// <summary>
        /// 刷新底部总状态的显示
        /// </summary>
        private void RefreshTotalLabelUI()
        {
            // up calculate total online/offline miners and hashrate.
            int onlineTotal = 0, offlineTotal = 0, connectingTotal =0;
            double hashrateTotal = 0;
            foreach (Miner item in minerDict.Values)
            {
                string itemStatus = item.getState();
                if (itemStatus == "online")
                {
                    onlineTotal++;
                    if (item.Dat != null && item.Dat.hashrate != null && item.Dat.hashrate.Length > 0)
                    {
                        double[] values = item.Dat.hashrate;
                        hashrateTotal += values[0];
                    }
                }else if (itemStatus == "offline")
                {
                    offlineTotal++;
                }
                else if(itemStatus == "connecting")
                {
                    connectingTotal++;
                }
            }
            totalStatusLabel.Text = $"总算力: {hashrateTotal.ToString().ToHashrate()} 在线: {onlineTotal}  离线: {offlineTotal}  链接: {connectingTotal}";
        }

        private void RefreshSelectedItemStatusUI()
        {
            int total = 0, selected = 0;
            for (int i = 0; i < minerGridView.Rows.Count; i++)
            {
                if ((Convert.ToBoolean(minerGridView.Rows[i].Cells[0].Value) == true))
                {
                    selected++;
                }
                total++;
            }
            if(selected == 0)
            {
                selectedTxt.Text = $"总共: {total} ";
            }
            else
            {
                selectedTxt.Text = $"总共: {total} 已选中: {selected}";
            }
        }

        // 释放资源
        public void Dispose()
        {
            if(refreshUITimer != null)
            {
                refreshUITimer.Close();
            }
            if (heartbeatTimer != null)
            {
                heartbeatTimer.Close();
            }
            if (reconnectTimer != null)
            {
                reconnectTimer.Close();
            }

            // release miner
            foreach (Miner miner in existMinerSet)
            {
                miner.StopWatch();
            }
        }

        private void checkAllBox_CheckedChanged(object sender, EventArgs e)
        {
            if (checkAllBox.Checked)
            {
                selectAll_Click(sender, e);
            }
            else
            {
                cancelAll_Click(sender, e);
            }
            RefreshSelectedItemStatusUI();
        }

        private void selectAll_Click(object sender, EventArgs e)
        {
            //遍历datagridview中的每一行，判断是否选中，若为选中，则选中
            for (int i = 0; i < minerGridView.Rows.Count; i++)
            {
                if ((Convert.ToBoolean(minerGridView.Rows[i].Cells[0].Value) == false))
                {
                    minerGridView.Rows[i].Cells[0].Value = true;
                }
                else
                    continue;
            }
        }

        private void cancelAll_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < minerGridView.Rows.Count; i++)
            {
                if ((Convert.ToBoolean(minerGridView.Rows[i].Cells[0].Value) == true))
                {
                    minerGridView.Rows[i].Cells[0].Value = false;
                }
                else
                    continue;
            }
        }

        private void minerConfigBtn_Click(object sender, EventArgs e)
        {
            // check condition
            List<Miner> selectedMiners = new List<Miner>();
            for (int i = 0; i < minerGridView.Rows.Count; i++)
            {
                if ((Convert.ToBoolean(minerGridView.Rows[i].Cells[0].Value) == true))
                {
                    if (minerGridView.Rows[i] == null || minerGridView.Rows[i].Cells[3].Value == null) continue;
                    string ip = minerGridView.Rows[i].Cells[3].Value.ToString();
                    if (ip == null) continue;

                    Miner miner = minerDict[ip];
                    if(miner != null && miner.Conf != null)
                    {
                        selectedMiners.Add(miner);
                    }
                }
            }
            if(selectedMiners.Count == 0)
            {
                MessageBoxEx.Show(this, "请选择要配置的矿机");
                return;
            }

            // TODO 关于名字的处理

            MinerSetting dailog = new MinerSetting(selectedMiners);
            dailog.StartPosition = FormStartPosition.CenterParent;
            dailog.ShowDialog();
        }

     
    }
}
